// 使用环形数的方式来作为滑动窗口的底层存储结构
package windows

import (
	"fmt"
	"sync/atomic"
	"time"
)

type block struct {
	data *bucket
	tail int
	head int
}

func NewBlock(data *bucket, head, tail int) *block {
	b := &block{}
	b.data = data
	b.head = head
	b.tail = tail
	return b
}

func (b *block) GetHead() int {
	return b.head
}

func (b *block) GetTail() int {
	return b.tail
}

func (b *block) GetBucket() *bucket {
	return b.data
}

func (b *block) Reset() {
	if b != nil {
		//TODO 所有counter都RESET一遍
		b.data.GetAdder().Reset()
	}
}

type BucketCircularArray struct {
	atv     atomic.Value
	blocks  []*block
	length  int
	hot     int32 //正被使用的block
	timer   *time.Ticker
	closeCh chan int
}

func NewBucketCircularArray(length int, interval time.Duration, opts ...BucketOption) *BucketCircularArray {
	var bs []*block
	if length > 0 {
		bs = make([]*block, length)
		for i := 0; i < length; i++ {
			bs[i] = NewBlock(NewBucket(opts...), i-1, i+1)
		}
		bs[0].head = length - 1
		bs[length-1].tail = 0
	}
	fmt.Printf("")
	timer := time.NewTicker(interval)
	bca := &BucketCircularArray{
		blocks:  bs,
		length:  length,
		timer:   timer,
		closeCh: make(chan int, 1),
	}

	bca.atv.Store(bca.blocks)
	go bca.LoopWithTime()
	return bca
}

func (b *BucketCircularArray) Sum() int {
	blocks := b.atv.Load()
	sum := 0
	if bs, ok := blocks.([]*block); ok {
		for _, v := range bs {
			sum += v.data.GetAdder().Sum()
		}
	}
	return sum
}

func (b *BucketCircularArray) Average() int {
	return b.Sum() / b.length
}

func (b *BucketCircularArray) GetCurBlock() *block {
	bs := b.atv.Load()
	blocks := bs.([]*block)
	i := int(atomic.LoadInt32(&(b.hot)))
	return blocks[i]
}

func (b *BucketCircularArray) String() string {
	s := fmt.Sprintf("hot index =%v ", atomic.LoadInt32(&(b.hot)))
	block := b.GetCurBlock()
	sum := b.Sum()
	s += fmt.Sprintf("data=%v sum %v average=%v", block.data.GetAdder().Sum(), sum, sum/b.length)
	return s
}

func (b *BucketCircularArray) Stop() {
	b.closeCh <- 1
}

func (b *BucketCircularArray) LoopWithTime() {
	for {
		select {
		case <-b.timer.C:
			old := b.GetCurBlock()
			next := old.GetTail()
			bs := b.atv.Load()
			blocks := bs.([]*block)
			blocks[next].Reset()
			b.atv.Store(blocks)
			atomic.StoreInt32(&(b.hot), int32(next))
		case <-b.closeCh:
			b.timer.Stop()
			return
		}
	}
}
